
#include <BPainter>
#include <BRender>
#include <BViewer>
#include <BScene>
#include <BCone>
#include <BCube>
#include <BImage>
#include <BMaterial>
#include <BReadWrite>
#include <BMouseEvent>

#include "SupportVertexHandler.h"

SupportVertexHandler::SupportVertexHandler()
{
	_hovered = false;
	_selected = false;

	_node = new BBody("nodeA");

	_supportPanel = new SupportVertexPanel();

	BShape* shape = _supportPanel->shape();
	_node->insertShape(shape);

	_line.setEnd(_supportPanel->length());
	connect(_supportPanel, "shape-changed", &SupportVertexHandler::slotShapeChanged);
	connect(_supportPanel, "rotation-changed", &SupportVertexHandler::slotRotationChanged);
}
SupportVertexHandler::~SupportVertexHandler()
{

}

void SupportVertexHandler::slotShapeChanged(BObject* object, const BValue& value)
{
	BShape* shape = value;
	_node->clearShapes();
	_node->insertShape(shape);
	BVector rotation = _supportPanel->rotation();
	shape->setEuler(rotation.x(), rotation.y(), rotation.z());
}
void SupportVertexHandler::slotRotationChanged(BObject* object, const BValue& value)
{
	if (BShape* shape = _node->shape(0))
	{
		BVector rotation = value;
		shape->setEuler(rotation.x(), rotation.y(), rotation.z());

		BReal sinz = sin(rotation.z());
		BReal siny = sin(rotation.y());
		BReal sinx = sin(rotation.x());

		BReal cosz = cos(rotation.z());
		BReal cosy = cos(rotation.y());
		BReal cosx = cos(rotation.x());

		BReal Rx[] = {
			1,		0,		0,
			0,		cosx,	sinx,
			0,		-sinx,	cosx
		};

		BReal Ry[] = {
			cosy,	0,		-siny,
			0,		1,		0,
			siny,	0,		cosy
		};

		BReal Rz[] = {
			cosz,	 sinz,	0,
			-sinz,	cosz,	0,
			0,		0,		1,
		};

		//BReal ma[3] = { cosy * cosz,						cosy * sinz,							-siny };
		//BReal mb[3] = { sinx * siny * cosz - cosx * sinz,	sinx * siny * sinz + cosx * cosz,		sinx * cosy };
		//BReal mc[3] = { siny * cosx * cosz + sinx * sinz,	siny * cosx * sinz - sinx * cosz,		cosx * cosy };

		BReal a[3] = { cosz * cosy,		sinz * cosx + sinx * siny * cosz,		sinx * sinz - siny * cosz * cosx };
		BReal b[3] = { -sinz * cosy,	cosz * cosx - sinz * siny * sinx,		cosz * sinx + cosx * sinz * siny };
		BReal c[3] = { siny,			-sinx * cosy,							cosy * cosx };

		_matrix.setRow(0, a[0], a[1], a[2]);
		_matrix.setRow(1, b[0], b[1], b[2]);
		_matrix.setRow(2, c[0], c[1], c[2]);
		_matrix.setEuler(rotation);

		shape->setMatrix(_matrix);
	}
}

void SupportVertexHandler::attached(BViewer* viewer)
{
	BScene* dscene = viewer->scene();
	_node->setPosition(0, 0, 0);
	dscene->insert(_node);
	this->emit("edit-object", _node);
	this->emit("append-panel", _supportPanel);
}
void SupportVertexHandler::detached(BViewer* viewer)
{
	BScene* dscene = viewer->scene();
	dscene->remove(_node);
	this->emit("remove-panel", _supportPanel);
}

void SupportVertexHandler::paint(BPainter& painter)
{
	const BWidget* widget = painter.widget();

	const BReal threshold = 0.001;

	BVector euler = _matrix.euler();
	BReal roll = euler.x();
	BReal pitch = euler.y();
	BReal yaw = euler.z();

	BString text;
	text << "roll\t" << roll << '\n';
	text << "pitch\t" << pitch << '\n';
	text << "yaw\t" << yaw << '\n';
	painter.setColor(200, 200, 200);
	painter.drawText(painter.clip(), text, Align_LeftBottom);
}
void SupportVertexHandler::render(BRender& render)
{
	render.setLighting(false);
	render.setDepthTest(false);
	render.setLineSmooth(true);
	render.setLineWidth(1);

	render.setColor(200, 0, 0);
	render.drawLine(_line);

	render.setPointSize(8);
	if (_selected)
		render.setColor(220, 0, 0);
	else if (_hovered)
		render.setColor(0, 220, 0);
	else
		render.setColor(0, 0, 220);
	render.drawVertex(_line.end());

	if (BShape* shape = _node->shape(0))
	{
		BVector direct = _line.direct();
		direct = shape->matrix().inverse().mult3(direct);
		BVector vertex = shape->vertex(direct);
		render.setPointSize(8);
		render.setColor(220, 0, 220);
		render.drawVertex(vertex * shape->matrix());
	}

	BVector axisX = _matrix.row(0);
	BVector axisY = _matrix.row(1);
	BVector axisZ = _matrix.row(2);
	render.setLineStipple(0xff00);
	render.setColor(220, 0, 0);
	render.drawLine(0, axisX);
	render.setColor(0, 220, 0);
	render.drawLine(0, axisY);
	render.setColor(0, 0, 220);
	render.drawLine(0, axisZ);
	render.setLineStipple(0x0000);

}

void SupportVertexHandler::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (_hovered)
	{
		_hovered = false;
		this->fresh();
	}
}
void SupportVertexHandler::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (_hovered)
	{
		_hovered = false;
		this->fresh();
	}
}
void SupportVertexHandler::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (_hovered)
	{
		const BPoint& pos = mouseEvent.pos();
		BLine line = this->viewer()->line(pos);
		_plane.set(line.direct(), _line.end());
		_hovered = false;
		_selected = true;
		this->fresh();
		mouseEvent.accept();
	}
}
void SupportVertexHandler::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (_selected)
	{
		_selected = false;
		this->fresh();
		mouseEvent.accept();
	}
}
void SupportVertexHandler::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	BLine line = this->viewer()->line(pos);
	if (_selected)
	{
		BVector point;
		if (line.intersect(_plane, point))
		{
			_line.setDirect(point.normal());
			this->fresh();
		}

		BMatrix calculatedTransformA;
		BVector calculatedAxis0 = calculatedTransformA.row(0);
		BVector calculatedAxis1 = calculatedTransformA.row(1);
		BVector calculatedAxis2 = calculatedTransformA.row(2);

		BMatrix calculatedTransformB;

		BVector axisBX = calculatedTransformB.row(0);
		BVector axisBY = calculatedTransformB.row(1);
		BVector axisBZ = calculatedTransformB.row(2);

		//BVector axis = calculatedTransformB.row(0);
		BVector axis = _line.direct();
		BReal roll = atan2(axisBY.dot(calculatedAxis2), axisBY.dot(calculatedAxis1));
		BReal pitch = atan2(axis.dot(-calculatedAxis2), axis.dot(calculatedAxis0));
		BReal yaw = atan2(axis.dot(calculatedAxis1), axis.dot(calculatedAxis0));

		_supportPanel->setRotation(roll, pitch, yaw);

		mouseEvent.accept();
	}
	else
	{
		bool hovered = false;
		BReal dist = line.distance(_line.end());
		BReal unit = this->camera()->unit(_line.end());
		if (dist < unit * 10)
		{
			hovered = true;
		}
		if (_hovered != hovered)
		{
			_hovered = hovered;
			this->fresh();
		}
	}
}
